/**
*
* @package MoltX QuickChat Client
* @author $Author: moltendorf $
* @version $Id: molten_chat.js 178 2009-09-26 06:58:55Z moltendorf $
* @latest $URL: https://molten-chat.googlecode.com/svn/trunk/source/styles/prosilver/template/molten_chat.js $
* @copyright (c) 2005-2009 Matthew Oltendorf
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/

// JavaScript Document

// ----- !THIS LIBRARY IS BEING REPLACED IN THE NEAR FUTURE, MAKING MODIFICATIONS TO IT MAY BE COMPLETELY INNACURATE WITHIN THE NEXT MONTH OR TWO WITH BETA RELEASE ONE! ----- \\

/*
	Unfinished Items:
		1.
		2.
		3.
		4.
		5.
*/

/*
	Known Issues:
		1.	Does not work on load balanced server clusters; must be run on a single server. This is due to the shared memory cache.
		2.	Is not an actual factory.
		3.
		4.
		5.
*/

// Activate logging:	^(\s*)/\*(log.*)\*\/$
//	   Replace with:	$1$2

// Deactivate logging:	^(\s*)(log.*)$
//		 Replace with:	$1/*$2*/

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: molten_chat.
// Type: Factory.
// Title: MoltX Chat Ajax Object.
// Description: Designed to be included and used to retrieve and display XML data from the MoltX Chat Server Object.
// Variables: history, server_retrieve, server_update, username, history_rate, form, message, server_send, users, server_online, users_rate, users_multiplier
//		1)	history: The element in which to store all chat history; Required!
//		2)	server_retrieve: The URL in which to request the XML document containing all new messages from; Required!
//		3)	server_update: The URL in which to send the POST data containing most recent message identifier received; Required!
//		4)	username: The username of the local user.
//		5)	history_rate: The rate at which to update the chat history (in milliseconds); Default: 5000. Use true for continuous asynchronous exchange.
//		6)	form: The form element containing both the message box, and the button.
//		7)	message: The element in which to retrieve all chat messages to be sent; Required if form is defined!
//		8)	server_send: The URL in which to send the POST data containing the chat message to; Required if message is defined!
//		9)	users: The element in which to populate with all online users.
//		10)	server_online: The URL in which to request the XML document containing all online users from; If empty, client-side user tracking will be used.
//		11)	users_rate: The rate at which to update the user list; Default: 30000; Does not work if client-side user tracking is used.
//		12)	users_multiplier: The rate at which to degrade both history_rate, and users_rate speed at as more users join the room; Default: 1; Does not work if client-side user tracking is used. Does not work if continuous asynchronous exchange is used.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
var molten_chat = function ()
	{
		// Default variables.
		this.online = true;
		this.connected = false;

		// Counters.
		this.action = 0;
		this.distance = 0;
		this.instant = false;
		this.jumpedToBottom = false;
		this.level = 0;
		this.line = 0;
		this.position = 0;
		this.receiving = false;
		this.scrolling = false;

		this.instance = 'new';

		this.scrollDistance = 2; // Higher means faster scrolling.
		this.scrollJump = 200; // The distance at which the scroller bypasses smooth scrolling, and rather just jumps to the bototm; false to disable.
		this.scrollMaximumMultiplier = 100; // This is the maximum speed at which the scroller will smoothly scroll.
		this.scrollMultiplierThreshold = 250; // Lower means faster scrolling.
		this.scrollSpeed = 25; // Lower is faster, and smoother, but more system taxing; Higher is slower, and more jumpy, but less system taxing.
		this.scrollingThreshold = 50; // The distance from the bottom that the scroller auto-scrolls to the bottom.

		// XMLHttpRequest object.
		this.connection = false;

		this.continuousAsynchronous = false;

		this.floodBackgroundColor = false;
		this.floodBorderColor = false;
		this.floodFlashColor = "rgb(206,42,78)";
		this.floodLastSent = 0;
		this.floodLimit = 1;
		this.floodNormalColor = false;
		this.floodTimer = false;

		this.history = false;
		this.historyRetriveURL = false;
		this.historyUpdateURL = false;
		this.historyUpdateRate = 5000;

		this.form = false;
		this.message = false;
		this.messageSendURL = false;

		this.username = false;

		this.users = false;
		this.usersKey = new Array ();
		this.usersList = new Array ();
		this.usersLocalColor = "#00cc00";
		this.usersOtherColor = "rgb(255,255,255)";
		this.usersOfflineThreshold = 120;
		this.usersRetrieveURL = false;
		this.usersUpdate = false;
		this.usersUpdateRate = 30000;
		this.usersUpdateMultiplier = 1;

		this.timeOffset = 0;

		this.months = Array ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
		this.weekdays = Array ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.Initialize = function (history, server_retrieve, server_update, username, history_rate, form, message, server_send, users, server_online, users_rate, users_multiplier)
	{
		// Run checks, and re-set variables that have been redefined.
		if (history != "" && server_retrieve != "" && server_update != "")
			{
				this.history = history;
				this.historyRetrieveURL = server_retrieve;
				this.historyUpdateURL = server_update;

				if (history_rate != "" && history_rate != true)
					{
						this.historyUpdateRate = history_rate;
					}
				else if (history_rate === true)
					{
						this.historyUpdateRate = false;
						this.continuousAsynchronous = true;
					}

				if (username != "")
					{
						this.username = username;
					}

				if (form != "")
					{
						if (message != "" && server_send != "")
							{
								this.form = form;
								this.message = message;
								this.messageSendURL = server_send;

								if (users != "")
									{
										this.users = users;

										if (server_online != "")
											{
												this.usersRetrieveURL = server_online;
												this.usersUpdateRate = users_rate;
												this.usersUpdateMultiplier = users_multiplier;
											}
									}

								// Set up the message input system to be easy to use.
								this.form.onsubmit = function () {return (chat.submitMessage ());}

								// Select the message box.
								this.message.select ();
							}
						else
							{
								// Lock down this object.
								this.online = false;
								alert ("Insufficient parameters.");
							}
					}
			}
		else
			{
				// Lock down this object.
				this.online = false;
				alert ("Insufficient parameters.");
			}

		// Connect to the chatserver.
		this.retrieve ();
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.Offline = function (url)
	{
		if (this.online == true)
			{
				// Create a new connection.
				this.connection = new this.server ();
				this.connection.open ("GET", url, true);

				// Complete the transaction.
				this.connection.send (null);

				this.online = false;
				this.connected = false;
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.Colors = function (local, other)
	{
		if (this.color (local) != "")
			{
				this.usersLocalColor = this.color (local);
			}

		if (this.color (other) != "")
			{
				this.usersOtherColor = this.color (other);
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.connect = function ()
	{
		/*log ("molten_chat.connect: called");*/
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.disconnect = function ()
	{
		/*log ("molten_chat.disconnect: called");*/
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.unixTime = function ()
	{
		/*log ("molten_chat.unixTime: called");*/

		// Creates a unix timestamp.
		return (Math.round ((new Date).getTime () / 1000));
	}

molten_chat.prototype.doubleIt = function (input)
	{
		input = '' + input;
		
		if (input.length > 1)
			{
				return input;
			}
		else
			{
				return ('0' + input);
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.localizeTime = function (time)
	{
		/*log ("molten_chat.localizeTime: called");*/

		// Creates a unix timestamp.
		return (time - this.timeOffset);
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.encode = function (unfiltered)
	{
		/*log ("molten_chat.encode: called");*/

		// The Javascript escape and unescape functions do not correspond with what browsers actually do.
		var safe = "0123456789" +			// Numeric.
			"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic; Upper-Case.
			"abcdefghijklmnopqrstuvwxyz" +	// Alphabetic; Lower-Case.
			"-_.!~*'()";					// RFC2396 Mark characters.

		var hexadecimal = "0123456789ABCDEF";	// Full set of hexadecimal characters.

		var encoded = "";
		unfiltered = "" + unfiltered;

		for (var i = 0; i < unfiltered.length; i++)
			{
				var character = unfiltered.charAt (i);
				if (character == " ")
					{
						encoded += "+";	// Convert spaces to "+" rather than "%20."
					}
				else if (safe.indexOf (character) != (-1))
					{
						encoded += character; // Add the character if it is considered "safe."
					}
				else
					{
						var characterCode = character.charCodeAt (0);
						if (characterCode > 255)
							{
								encoded += character; // Substitute a space if the character code is not 8-bit.
							}
						else
							{
								encoded += "%";
								encoded += hexadecimal.charAt ((characterCode >> 4) & (0xF));
								encoded += hexadecimal.charAt (characterCode & (0xF));
							}
					}
			}

		return (encoded);
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.server = function ()
	{
		/*log ("molten_chat.server: called");*/

		var object = false;

		// Branch for native XMLHttpRequest object.
		if (window.XMLHttpRequest && !(window.ActiveXObject))
			{
				try
					{
						object = new XMLHttpRequest ();
					}
				catch (e)
					{
						object = false;
					}
			}
		// Branch for Internet Explorer/Windows ActiveX version. -- Wonky!
		else if (window.ActiveXObject)
			{
				try
					{
						object = new ActiveXObject ("Msxml2.XMLHTTP");
					}
				catch (e)
					{
						try
							{
        						object = new ActiveXObject ("Microsoft.XMLHTTP");
							}
						catch (e)
							{
								object = false;
							}
					}
			}

		return (object);
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.color = function (color)
	{
		/*log ("molten_chat.color: called");*/

		/*
		if (color.length == 7)
			{
				return (color);
			}
		else if (color.length == 6)
			{
				return ("#" + color);
			}
		else
			{
				return ("");
			}
		*/

		return ('rgb(' + color + ')');
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.client = function ()
	{
		/*log ("molten_chat.client: called");*/

		if (this.connection.readyState == 4 && this.online == true)
			{
				/*log ("molten_chat.client: success");*/

				this.receiving = false;

				if (this.connection.status == 200 && this.connection.responseText != 0 && this.connection.responseText != 1)
					{
						// Send the response to the response handling function.
						this.response (this.connection.responseXML);
					}
				else
					{
						//this.retrieve ();
					}

				this.retrieve ();

				/*
				else if (this.connected == false)
					{

						/*log ("molten_chat.response: page cache error");*/
						/*log ("molten_chat.response: manually resetting cache");*/
						/*
						// Reset the line count.
						//this.update (0);
						this.action = 0;
						this.level = 0;
						this.line = 0;

						// Unlock the receiving state.
						this.receiving = false;

						// Since the retrieve function hasn't automatically started looping, we fire it off again.
						this.retrieve ();
					}
				/*

				/*if (this.instant == false)
					{
						/*log ("molten_chat.client: no longer receiving data");*/

						// Unlock request methods regardless of if we got a successful response or not.
						//this.receiving = false;
						/*
					}
				else
					{
						/*log ("molten_chat.client: retrieving messages");*/
						/*

						// Retrieve another copy of messages immediately, but do not trigger auto response.
						//this.retrieve (true);

						/*log ("molten_chat.client: set instantaneous retrieval to false");*/
						/*this.instant = false;
					}*/
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
/*
molten_chat.prototype.response = function (response)
	{
		/*log ('molten_chat.response: called');*/
		/*
		// One time use.
		var connected = false;

		// Get data elements.
		var data = response.getElementsByTagName ('data'), info;
		for (var i = 0; i < data.length; i++)
			{
				info [data [i].getAttribute ('type')] = data [i].getAttribute ('value');
			}

		// Get current scroll height, so we can test it later, and auto-scroll if the user is near the bottom of the history.
		var position = this.history.scrollHeight > 0 ?
			this.history.scrollHeight :
			this.history.offsetHeight;

		// Get messages element.
		var messages = response.getElementsByTagName ('messages');

		// Filter out really delayed responses from the server by checking the line number.
		if (messages.length > 0 && (this.level < info ['level'] || this.line < info ['line']))
			{
				// Do stuff.
			}
	}
*/

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.response = function (response)
	{
		/*log ('molten_chat.response: called');*/

		// One time use.
		var connected = false, username, time, user, i, received;

		// Get data elements.
		var data = response.getElementsByTagName ('data'), info = new Array ();

		/*log ('molten_chat.response: got data elements');*/

		for (i = 0; i < data.length; i++)
			{
				/*log ('molten_chat.response: got data ' + data [i].getAttribute ('type') + '=' + data [i].getAttribute ('value'));*/

				info [data [i].getAttribute ('type')] = parseInt (data [i].getAttribute ('value'), 10);
			}

		this.timeOffset = (info ['time'] - parseInt (this.unixTime (), 10));
		this.instance = info ['instance'];

		//log ('molten_chat.response: got info array; ' + info);

		// Get current scroll height, so we can test it later, and auto-scroll if the user is near the bottom of the history.
		var position = this.history.scrollHeight > 0 ?
			this.history.scrollHeight :
			this.history.offsetHeight;

		// Get actions element.
		var actions = response.getElementsByTagName ('actions');

		if (actions.length > 0)
			{
				/*log ('molten_chat.response: got actions element');*/

				var action = actions [0].getElementsByTagName ('action');
				var amessage;

				for (i = 0; i < action.length; i++)
					{
						received = action [i].getAttribute ('type');
						amessage = (action [i].childNodes.length > 0) ?
							action [i].firstChild.nodeValue :
							'';

						if (received == "disconnect" && this.online == true)
							{
								/*log ("molten_chat.response: disconnected");*/

								// Remove the local user from the user's list to aid in the "visual impact" of disconnecting. :P
								this.users.removeChild (this.usersList [this.username] ["element"]);

								// Most functions check if this is false before proceeding with server queries.
								this.online = false;
							}
						else if (received == "connect" && this.connected == false)
							{
								/*log ("molten_chat.response: connected");*/

								// Update the line number.
								//this.update (line);
								//this.action = 0;
								//this.level = 0;
								//this.line = 0;

								// Now get the userlist.
								//this.instant = true;
								//this.usersUpdate = true;
								connected = true;
								this.receiving = false;

								/*log ("molten_chat.response: running retrieval; forced");*/
								// Run retrieve, since it didn't automatically que itself up with the this.connected variable set to false earlier.
								//this.retrieve (true);

								// Remove the "Connecting..." from the history, and users elements.
								if (this.history.childNodes.length > 1)
									{
										this.history.removeChild (this.history.childNodes [1]);
										this.users.removeChild (this.users.childNodes [1]);
									}
								else
									{
										this.history.removeChild (this.history.childNodes [0]);
										this.users.removeChild (this.users.childNodes [0]);
									}

								// Add the local user to the top of the userlist.
								this.usersKey.push (this.username);
								this.usersList [this.username] = new Array ();

								// Create the element that will "store" the username; We do not use a local variable as this aids in removing the element on the fly later.
								this.usersList [this.username] ["element"] = document.createElement ("p");
								this.usersList [this.username] ["element"].appendChild (document.createTextNode (this.username));

								// The green color is exclusive to the local user to help him understand who is who.
								this.usersList [this.username] ["element"].style.color = this.usersLocalColor;

								// Append this user to the user element.
								this.users.appendChild (this.usersList [this.username] ["element"]);
							}

						if (amessage != '')
							{
								// Create the message element.
								message = document.createElement ('p');
								message.appendChild (document.createElement ('span'));
								message.appendChild (document.createElement ('span'));
								message.childNodes [0].appendChild (document.createTextNode (amessage));
								message.style.fontWeight = "bold";
								this.history.appendChild (message);

								// Scroll to the bottom!
								this.bottom (true);
							}
					}
			}

		// Get messages element.
		var messages = response.getElementsByTagName ('messages');

		// Filter out really delayed responses from the server by checking the line number.
		if (messages.length > 0 && (this.level < info ['level'] || this.line < info ['line']))
			{
				/*log ('molten_chat.response: got messages element');*/

				var color, message, message_array, start, link, href, thisTime = new Date ();
				var thisDay = new Date (), thisWeek = new Date (), thisYear = new Date ();
				thisDay.setHours (thisDay.getHours () - 24);
				thisDay = (Math.round (thisDay.getTime () / 1000) - thisDay.getTimezoneOffset ());
				thisWeek.setDate (thisWeek.getDate () - 7);
				thisWeek = (Math.round (thisWeek.getTime () / 1000) - thisWeek.getTimezoneOffset ());
				thisYear.setMonth (thisYear.getMonth () - 12);
				thisYear = (Math.round (thisYear.getTime () / 1000) - thisYear.getTimezoneOffset ());

				// Get a list of message nodes from the XML document.
				message_array = messages [0].getElementsByTagName ('message');

				/*log ('molten_chat.response: got ' + message_array.length + ' messages');*/

				// Set the position we are starting the loop at to zero.
				start = 0;

				if (this.level != 0 || this.line != 0)
					{
						start = (message_array.length - ((info ['limit'] * info ['level'] + info ['line']) - (info ['limit'] * this.level + this.line)));
						start = (start >= 0) ?
							start :
							0;
					}

				/*log ('molten_chat.response: starting loop from ' + start);*/

				// Update local information.
				this.level = info ['level'];
				this.line = info ['line'];

				// Check if the line number is set or not.
				/*if (info ['line'] != (-1))
					{
						// Since we use a method of sending all messages not accounted for, the server may send messages that have already been displayed.
						// We start a bit further in the loop if the server didn't receive the line number we sent back to them.
						if ((info ['line'] - this.line) <= message_array.length)
							{
								start = (message_array.length - ((info ['limit'] * info ['level'] + info ['line']) - (info ['limit'] * this.level + this.line)));
							}



						// Return the message number to have the server update it in the database.
						// The script would run fine without this, but if the server sends all the messages each time, the amount of lag would be insane.
						/*
						if (this.connected == true)
							{
								this.update (line);
							}
						*/
					//}

				for (i = start; i < message_array.length; i++)
					{
						//log ('molten_chat.response: loop');

						// Get the username of the person who sent this message.
						user = message_array [i].getElementsByTagName ('by');
						username = (user.length > 0) ?
							 user [0].firstChild.nodeValue :
							 '';

						// Create the message element.
						message = document.createElement ('p');

						link = message_array [i].getAttribute ('link');
						link = (typeof (link) == 'string') ?
							link :
							false;

						if (username != '' || !link)
							{
								message.appendChild (document.createElement ('span'));
								message.appendChild (document.createElement ('span'));
							}
						else
							{
								message.appendChild (document.createElement ('a'));
								//href = document.createAttribute ('href');
								//href.appendChild(document.createTextNode (link));
								//message.childNodes [1].appendChild (href);
								message.childNodes [0].setAttribute ('href', link);
							}

						// Check if this was a server generated message, or not.
						if (username != '' && (this.connected == true || connected == true))
							{
								// Append the username to the message element.
								message.childNodes [1].appendChild (document.createTextNode ('[' + username + ']:\u00a0'));
								message.childNodes [1].style.fontWeight = 'bold';

								if (!link)
									{
										// Create another message element to store the message in.
										message.appendChild (document.createElement ('span'));
									}
								else
									{
										// Create another message element to store the message in.
										message.appendChild (document.createElement ('a'));
										//href = document.createAttribute ('href');
										//href.appendChild(document.createTextNode (link));
										//message.childNodes [1].appendChild (href);
										message.childNodes [2].setAttribute ('href', link);
									}

								// Append the message body to the message element.
								message.childNodes [2].appendChild (document.createTextNode (message_array [i].getElementsByTagName ('content') [0].firstChild.nodeValue));
								//message.childNodes [1].appendChild (messages [i].getElementsByTagName ("content") [0]);

								//log ("molten_chat.response: appended \"" + messages [i].getElementsByTagName ("content") [0].firstChild.nodeValue + "\" to history");

								/*
								// Support for tags within the content section... Not working. :/
								// Completely breaks support for Internet Explorer as well at this point.
								var childs = messages [i].getElementsByTagName ("content") [0].childNodes;
								if (childs [0].childNodes.length <= 1)
									{
										//childs = messages [i].getElementsByTagName ("content") [1].childNodes;
									}
								for (var j = 0; j < childs.length; j++)
									{
										if (childs [j].childNodes.length > 0)
											{
												message.childNodes [1].appendChild (childs [j]);
											}
										else
											{

											}
									}
								*/

								// Get the time the message was sent.
								//time = parseInt (message_array [i].getElementsByTagName ("time") [0].firstChild.nodeValue, 10);
								time = parseInt (message_array [i].getAttribute ('time'), 10);
								thisTime.setTime (time * 1000);

								if (time > thisDay)
									{
										// Display time: 18:00
										message.childNodes [0].appendChild (document.createTextNode (this.doubleIt (thisTime.getHours ()) + ':' + this.doubleIt (thisTime.getMinutes ()) + '\u00a0'));
									}
								else if (time > thisWeek)
									{
										// Display weekday: Fri
										message.childNodes [0].appendChild (document.createTextNode (this.weekdays [thisTime.getDay ()] + '\u00a0'));
									}
								else if (time > thisYear)
									{
										// Display date: 08 Aug
										message.childNodes [0].appendChild (document.createTextNode (this.doubleIt (thisTime.getDate ()) + '\u00a0' + this.months [thisTime.getMonth ()] + '\u00a0'));
									}
								else
									{
										// Display month: Aug 2010
										message.childNodes [0].appendChild (document.createTextNode (thisTime.getFullYear () + '\u00a0'));
									}

								// Get the color of the message sender's username.
								//color = message_array [i].getElementsByTagName ('color');
								color = user [0].getAttribute ('color');
								color = (typeof (color) == 'string') ?
									this.color (color) :
									'';

								// Check if the user that sent this message is the local one.
								if (username != this.username)
									{
										if (color == "")
											{
												// Set the color to local.
												color = this.usersOtherColor;
											}

										// Set the color to other.
										message.childNodes [1].style.color = color;

										// Send the username and the time to the handle users function.
										this.handleUser (username, time, color);
									}
								else
									{
										if (color == "")
											{
												// Set the color to local.
												color = this.usersLocalColor;
											}

										if (this.usersList [username] ["color"] != color && color != "" && typeof (color) == 'string')
											{
												// Set this user's color.
												this.usersList [username] ["color"] = color;
												this.usersList [username] ["element"].style.color = color;
											}

										// Set the color to local.
										message.childNodes [1].style.color = color;
									}

								// Add the messages to the end of the chat history.
								this.history.appendChild (message);
							}
						// The username variable is empty; we are expecting a server-generated message.
						else if (username == "")
							{
								// Action to perform.
								//var received = message_array [i].getElementsByTagName ("action") [0].firstChild.nodeValue;
								received = false;

								// Append the message body to the message element.
								message.childNodes [0].appendChild (document.createTextNode (message_array [i].getElementsByTagName ("content") [0].firstChild.nodeValue));
								message.style.fontWeight = "bold";


//								if (received == "reset" && this.connected == true)
//									{
//										log ("molten_chat.response: reset");
//
//										// Reset the line count.
//										this.action = 0;
//										this.level = 0;
//										this.line = 0;
//										//this.update (1);
//
//										// Add the messages to the end of the chat history.
//										this.history.appendChild (message);
//									}
//								else if (received != "reset")
//									{
//										if (received == "disconnect" && this.online == true)
//											{
//												log ("molten_chat.response: disconnected");
//
//												// Remove the local user from the user's list to aid in the "visual impact" of disconnecting. :P
//												this.users.removeChild (this.usersList [this.username] ["element"]);
//
//												// Most functions check if this is false before proceeding with server queries.
//												this.online = false;
//											}
//										else if (received == "connect" && this.connected == false)
//											{
//												log ("molten_chat.response: connected");
//
//												// Update the line number.
//												//this.update (line);
//												this.action = 0;
//												this.level = 0;
//												this.line = 0;
//
//												// Now get the userlist.
//												this.instant = true;
//												this.usersUpdate = true;
//												connected = true;
//
//												log ("molten_chat.response: running retrieval; forced");
//												// Run retrieve, since it didn't automatically que itself up with the this.connected variable set to false earlier.
//												this.retrieve (true);
//
//												// Remove the "Connecting..." from the history, and users elements.
//												if (this.history.childNodes.length > 1)
//													{
//														this.history.removeChild (this.history.childNodes [1]);
//														this.users.removeChild (this.users.childNodes [1]);
//													}
//												else
//													{
//														this.history.removeChild (this.history.childNodes [0]);
//														this.users.removeChild (this.users.childNodes [0]);
//													}
//
//												// Add the local user to the top of the userlist.
//												this.usersKey.push (this.username);
//												this.usersList [this.username] = new Array ();
//
//												// Create the element that will "store" the username; We do not use a local variable as this aids in removing the element on the fly later.
//												this.usersList [this.username] ["element"] = document.createElement ("p");
//												this.usersList [this.username] ["element"].appendChild (document.createTextNode (this.username));
//
//												// The green color is exclusive to the local user to help him understand who is who.
//												this.usersList [this.username] ["element"].style.color = this.usersLocalColor;
//
//												// Append this user to the user element.
//												this.users.appendChild (this.usersList [this.username] ["element"]);
//											}
//										else if (received == "refresh")
//											{
//												log ("molten_chat.response: refresh");
//
//												// Automatically refresh the page if a new build is available.
//												setTimeout ("window.location = window.location; ", (this.historyUpdateRate + this.historyUpdateRate));
//											}

										// Add the messages to the end of the chat history.
										this.history.appendChild (message);
									//}
							}
					}
			}

		// Get users element.
		var users = response.getElementsByTagName ('users');

		if (users.length > 0 && this.action < info ['action'])
			{
				/*log ('molten_chat.response: got users element');*/

				// Update local information.
				this.action = info ['action'];

				// Return to normal message updating.
				this.usersUpdate = false;

				var online = new Array ();
				user = users [0].getElementsByTagName ('user');

				/*log ('molten_chat.response: got ' + user.length + ' users');*/

				// Add and update any users flagged as online.
				for (i = 0; i < user.length; i++)
					{
						//username = user [i].getElementsByTagName ("username") [0].firstChild.nodeValue;
						username = user [i].firstChild.nodeValue;

						// Get the color of the message sender's username.
						//color = user [i].getElementsByTagName ('color');
						color = user [i].getAttribute ('color');
						color = (typeof (color) == 'string') ?
							this.color (color) :
							'';

						/*log ('molten_chat.response: added user ' + username);*/

						if (username != this.username)
							{
								if (color == "")
									{
										// Set the color to local.
										color = this.usersOtherColor;
									}

								// Set the status of this user locally to online.
								online [username] = true;

								// Get the time the user was last updated.
								time = user [i].getAttribute ('time');

								// Send the username and the time to the handle users function.
								this.handleUser (username, time, color);
							}
						else
							{
								if (color == "")
									{
										// Set the color to local.
										color = this.usersLocalColor;
									}

								if (this.usersList [username] ["color"] != color && color != "" && typeof (color) == 'string')
									{
										// Set this user's color.
										this.usersList [username] ["color"] = color;
										this.usersList [username] ["element"].style.color = color;
									}
							}
					}

				// Remove users from the userlist that were not flagged as online.
				for (i = 0; i < this.usersKey.length; i++)
					{
						if (this.usersList [this.usersKey [i]] ["active"] == true && typeof (online [this.usersKey [i]]) == "undefined")
							{
								this.removeUser (this.usersKey [i]);
							}
					}

				if (this.connected == false)
					{
						this.connected = true;

						/*log ("molten_chat.response: starting data retrieval method loop");*/

						// Start the history update function.
						//setTimeout ("chat.retrieve (); ", this.historyUpdateRate);

						// Start the user update function.
						setTimeout ("chat.scanUsers (); ", this.usersUpdateRate);
					}
			}

		// We never received a connect message. Even if we did receive messages, it does not mean that we got them all.
		// Reset line count to 0 because the page was not cache'd properly.
		if (connected == false && this.connected == false && this.online == true)
			{
				/*log ("molten_chat.response: page cache error");*/
				/*log ("molten_chat.response: manually resetting cache");*/

				// Reset the line count.
				//this.update (0);
				this.action = 0;
				this.level = 0;
				this.line = 0;

				// Unlock the receiving state.
				this.receiving = false;

				// Since the retrieve function hasn't automatically started looping, we fire it off again.
				//this.retrieve ();
			}

		// Get the height of the history box.
		var height = (this.history.style.pixelHeight) ?
			this.history.style.pixelHeight :
			this.history.offsetHeight;

		// Scroll to the bottom?
		if ((position - this.history.scrollTop - height) < this.scrollingThreshold || this.scrolling != false || this.online == false)
			{
				// Scroll to the bottom!
				this.bottom (true);
			}

		/*log ('molten_chat.response: calling retrieve');*/

		//this.retrieve ();
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.retrieve = function (force)
	{
		/*log ("molten_chat.retrieve: called");*/

		if (this.receiving == false || (this.instant == true && force == true))
			{
				/*log ('molten_chat.retrieve: sent request to server')*/

				// Get the URL we will be using to send the request for information to.
				var url = (this.usersUpdate == false || this.usersRetrieveURL == false) ?
					this.historyRetrieveURL + "&instance=" + this.instance + "&timestamp=" + this.unixTime () + "&level=" + this.level + "&line=" + this.line + "&action=" + this.action :
					this.usersRetrieveURL + "&instance=" + this.instance + "&timestamp=" + this.unixTime () + "&level=" + this.level + "&line=" + this.line + "&action=" + this.action;

				// We are in an active state of receiving data.
				this.receiving = true;

				// Create a new connection.
				this.connection = new this.server ();
				this.connection.open ("GET", url, true);

				// Define the function to call when we get data back.
				this.connection.onreadystatechange = function () {chat.client ();}

				// Complete the transaction.
				this.connection.send (null);
			}

		if (this.online == true && this.connected == true && force != true)
			{
				//setTimeout ("chat.retrieve (); ", this.historyUpdateRate);
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.update = function (input)
	{
		/*log ("molten_chat.update: called");*/

		// Format the message into POST data.
		var line = "line=" + this.encode (input);

		// Check if we're online and connected.
		if (this.online == true)
			{
				if (this.line < input || input == 0)
					{
						this.line = input;

						// Get the URL we will be using to send the information to.
						var url = this.historyUpdateURL + "&timestamp=" + this.unixTime () + "&level=" + this.level + "&line=" + this.line + "&action=" + this.action;

						/*log ("molten_chat.update: URL=" + url);*/

						// Create a new connection.
						var connection = new this.server ();
						connection.open ("POST", url, false);

						// Define the headers for a POST message.
						connection.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
						//connection.setRequestHeader ("Content-length", line.length);
						//connection.setRequestHeader ("Connection", "close");

						/*log ("molten_chat.update: send=" + input);*/

						// Send the message.
						connection.send (line);

						/*log ("molten_chat.update: received=" + connection.responseText);*/
					}
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.send = function (input)
	{
		/*log ("molten_chat.send: called");*/

		// Format the message into POST data.
		var message = "message=" + encodeURIComponent (input); //this.encode (input);

		if (this.online == true)
			{
				// Get the URL we will be using to send the information to.
				var url = this.messageSendURL + "&instance=" + this.instance + "&timestamp=" + this.unixTime ();

				// Scroll to the bottom!
				this.bottom (true);

				if (this.connected == true)
					{
						this.instant = true;
					}

				// Create a new connection.
				var connection = new this.server ();
				connection.open ("POST", url, true);

				// Define the headers for a POST message.
				connection.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
				connection.setRequestHeader ("Content-length", message.length);
				connection.setRequestHeader ("Connection", "close");

				// Send the message.
				connection.send (message);
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.handleUser = function (username, time, color)
	{
		/*log ("molten_chat.handleUser: called");*/

		// We do most of our user handling in this block here...
		// Check if this user has never sent a message before.
		if (typeof (this.usersList [username]) == "undefined")
			{
				// Add this user to the list of users that have sent a message.
				this.usersKey.push (username);
				this.usersList [username] = new Array ();

				// Create the element that will "store" the username; we do not use a local variable as this aids in removing the element on the fly later.
				this.usersList [username] ["element"] = document.createElement ("p");
				this.usersList [username] ["element"].appendChild (document.createTextNode (username));

				// Set this user's color.
				this.usersList [username] ["color"] = this.usersOtherColor;
				this.usersList [username] ["element"].style.color = this.usersOtherColor;

				// Set this user's activity as false since, for efficiency, we will check the most recent message time later.
				this.usersList [username] ["active"] = false;

				// Set the user's time to the current time.
				this.usersList [username] ["time"] = this.unixTime ();
			}

		// Check if the last time this user was updated is later then the time given.
		if (this.usersList [username] ["time"] < this.localizeTime (time))
			{
				// Set the last time a message was received.
				this.usersList [username] ["time"] = this.localizeTime (time);
			}

		if (this.usersList [username] ["color"] != color && color != "" && typeof (color) == 'string')
			{
				// Set this user's color.
				this.usersList [username] ["color"] = color;
				this.usersList [username] ["element"].style.color = color;
			}

		// Check if this user is set as inactive, and the time this message was received was within the last 5 minutes.
		if (username != this.username && this.localizeTime (time) > (this.unixTime () - this.usersOfflineThreshold) && this.usersList [username] ["active"] == false)
			{
				this.appendUser (username);
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.scanUsers = function ()
	{
		/*log ("molten_chat.scanUsers: called");*/

		var username;
		for (var i = 0; i < this.usersKey.length; i++)
			{
				username = this.usersKey [i];

				// Check if this user is set to active, and is not the local user.
				if (this.usersList [username] != this.username && this.usersList [username] ["active"] == true)
					{
						// Check when this user last sent a message to the server.
						if (this.usersList [username] ["time"] < (this.unixTime () - this.usersOfflineThreshold))
							{
								this.removeUser (username);
							}
					}
			}

		//this.usersUpdate = true;

		setTimeout ("chat.scanUsers (); ", this.usersUpdateRate);
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.appendUser = function (username)
	{
		/*log ("molten_chat.appendUser: called");*/

		// Set this user as active.
		this.usersList [username] ["active"] = true;

		// Append this user to the users element.
		this.users.appendChild (this.usersList [username] ["element"]);

		// Display a "this user has entered the room."
		if (this.connected == true)
			{
				var message;
				
				// Get current scroll height, so we can test it later, and auto-scroll if the user is near the bottom of the history.
				var position = this.history.scrollHeight > 0 ?
					this.history.scrollHeight :
					this.history.offsetHeight;

				// Create the message element.
				message = document.createElement ("p");

				// Add the message.
				message.appendChild (document.createTextNode (username + " has entered the room."));
				message.style.fontWeight = "bold";

				// Append the message to the history.
				this.history.appendChild (message);

				// Get the height of the history box.
				var height = (this.history.style.pixelHeight) ?
					this.history.style.pixelHeight :
					this.history.offsetHeight;

				// Scroll to the bottom?
				if ((position - this.history.scrollTop - height) < this.scrollingThreshold || this.scrolling != false || this.online == false)
					{
						// Scroll to the bottom!
						this.bottom (true);
					}
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.removeUser = function (username)
	{
		/*log ("molten_chat.removeUser: called");*/

		// Set this user as inactive.
		this.usersList [username] ["active"] = false;

		// Remove this user from the users element.
		this.users.removeChild (this.usersList [username] ["element"]);

		// Display a "this user has left the room."
		if (this.connected == true)
			{
				var message;

				// Get current scroll height, so we can test it later, and auto-scroll if the user is near the bottom of the history.
				var position = this.history.scrollHeight > 0 ?
					this.history.scrollHeight :
					this.history.offsetHeight;

				// Create the message element.
				message = document.createElement ("p");

				// Add the message.
				message.appendChild (document.createTextNode (username + " has left the room."));
				message.style.fontWeight = "bold";

				// Append the message to the history.
				this.history.appendChild (message);

				// Get the height of the history box.
				var height = (this.history.style.pixelHeight) ?
					this.history.style.pixelHeight :
					this.history.offsetHeight;

				// Scroll to the bottom?
				if ((position - this.history.scrollTop - height) < this.scrollingThreshold || this.scrolling != false || this.online == false)
					{
						// Scroll to the bottom!
						this.bottom (true);
					}
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.bottom = function (starting)
	{
		if (this.jumpedToBottom == true)
			{
				// Get the current scroll position.
				var position = this.history.scrollHeight > 0 ?
					this.history.scrollHeight :
					this.history.offsetHeight;

				// Get the height of the history box.
				var height = (this.history.style.pixelHeight) ?
					this.history.style.pixelHeight :
					this.history.offsetHeight;

				// Get the current pixels from the bottom of the history.
				var bottom = (position - this.history.scrollTop - height);

				// Check if we are already in a state of active smooth scrolling.
				if (this.scrolling === false)
					{
						/*log ("molten_chat.bottom: called");*/

						// Fool the next set of statements!
						starting = false;

						// Default speed.
						this.scrolling = 0;

						// Update our copy of the scroll position.
						this.position = position;
						this.distance = bottom;

						// Scale the speed of the scroll based on how high up we are from the bottom of the chat history.
						if (bottom > (this.scrollMaximumMultiplier * this.scrollMultiplierThreshold))
							{
								// But we check for higher because if it's above a certain size, we jump to the bottom instantly.
								if (bottom > (this.scrollJump * this.scrollMultiplierThreshold))
									{
										// Fool the next set of statements!
										starting = true;

										// We are not "scrolling" but rather "jumping."
										this.scrolling = false;

										// Scroll to the bottom, instantly!
										this.history.scrollTop = this.history.scrollHeight > 0 ?
											this.history.scrollHeight :
											this.history.offsetHeight;
									}
								else
									{
										// Set the scrolling rate to the maximum speed.
										this.scrolling = this.scrollMaximumMultiplier;
									}
							}
						else
							{
								// Set the speed at which we are scrolling; 1x for each threshold breached.
								while (bottom > (this.scrolling * this.scrollMultiplierThreshold) && this.scrolling <= this.scrollMaximumMultiplier)
									{
										// Increase the scrolling speed.
										this.scrolling++;
									}
							}
					}
				else if (starting == true)
					{
						/*log ("molten_chat.bottom: called");*/

						// Update the distance from the bottom.
						this.distance = bottom;
					}

				if (starting != true && this.distance == bottom && this.distance > 0)
					{
						// Are we at least above the minimum scrolling speed?
						if (this.scrolling != 0)
							{
								this.history.scrollTop += (this.scrollDistance * this.scrolling);

								this.distance -= (this.scrollDistance * this.scrolling);
							}
						else
							{
								// We bypass the distance, and rather do one pixel at a time for a dramatic look.
								this.history.scrollTop++;

								this.distance--;
							}

						// Repeat until finished! (Sound familiar, World of Warcraft players?)
						setTimeout ("chat.bottom (); ", this.scrollSpeed);
					}
				else if (this.distance != bottom || this.distance <= 0)
					{
						// We are no longer scrolling down. Reset the multiplier.
						this.scrolling = false;
					}
			}
		else
			{
				this.jumpedToBottom = true;

				// Scroll to the bottom, instantly!
				this.history.scrollTop = this.history.scrollHeight > 0 ?
					this.history.scrollHeight :
					this.history.offsetHeight;
			}
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.submitMessage = function ()
	{
		/*log ("molten_chat.submitMessage: called");*/

		// Is the message not empty?
		if (this.message.value != "" && this.floodLastSent < (this.unixTime () - this.floodLimit))
			{
				this.floodLastSent = this.unixTime ();

				// Send the message.
				this.send (this.message.value);

				// Clear the input box.
				this.message.value = "";

				// Select the message box.
				this.message.select ();
			}
		else
			{
				this.flashMessageBox (false);
			}

		return false;
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
molten_chat.prototype.flashMessageBox = function (existing)
	{
		/*log ("molten_chat.flashMessageBox(" + existing + "): called");*/

		if (typeof (existing) == 'number' || this.floodTimer == false)
			{
				existing = (typeof (existing) == 'number') ?
					existing :
					0;

				if ((existing % 2) == 0)
					{
						if (this.floodNormalColor == false)
							{
								this.floodNormalColor = this.message.style.color;
								this.floodBackgroundColor = this.message.style.backgroundColor;
								this.floodBorderColor = this.message.style.borderColor;
							}

						// Flood the box with this color.
						this.message.style.color = this.floodFlashColor;
						//this.message.style.backgroundColor = this.floodFlashColor;
						this.message.style.borderColor = this.floodFlashColor;

						this.floodTimer = setTimeout ('chat.flashMessageBox (' + (existing + 1) + ');', 100);
					}
				else
					{
						// Reset colors.
						this.message.style.color = this.floodNormalColor;
						this.message.style.backgroundColor = this.floodBackgroundColor;
						this.message.style.borderColor = this.floodBorderColor;

						if (existing < 4)
							{
								this.floodTimer = setTimeout ('chat.flashMessageBox (' + (existing + 1) + ');', 100);
							}
						else
							{
								this.floodTimer = false;
							}
					}
			}

		return true;
	}

var chat = new molten_chat ();

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
function log (message)
	{
		/*
		if (typeof (window.messages) != "undefined")
			{
				// Get the current log position.
				var position = window.messages.scrollHeight > 0 ?
					window.messages.scrollHeight :
					window.messages.offsetHeight;

				// Get the height of the log box.
				var height = (window.messages.style.pixelHeight) ?
					window.messages.style.pixelHeight :
					window.messages.offsetHeight;

				var message = document.createTextNode (message);
				var block = document.createElement ("p");

				block.appendChild (message);

				if (typeof (window.messages_toggle) != "undefined")
					{
						window.messages_toggle.style.display = "block";
					}
				else
					{
						window.messages.style.display = "block";
					}

				window.messages.appendChild (block);

				if ((position - window.messages.scrollTop - height) < 50)
					{
						// Scroll to the bottom, instantly!
						window.messages.scrollTop = window.messages.scrollHeight > 0 ?
							window.messages.scrollHeight :
							window.messages.offsetHeight;
					}
			}
		//*/
	}

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: foo
// Title: Generic.
// Variables: $foo
//	1.	$foo: Does nothing.
//		Default: 'foo'
// Description: Generic.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
function toggleLog ()
	{
		if (typeof (window.messages) != "undefined")
			{
				if (window.messages.style.display == "block")
					{
						window.messages.style.display = "none";
					}
				else
					{
						window.messages.style.display = "block";

						// Scroll to the bottom, instantly!
						window.messages.scrollTop = window.messages.scrollHeight > 0 ?
							window.messages.scrollHeight :
							window.messages.offsetHeight;
					}
			}
	}
